#ifndef TEST_GRAPHICS_TERRAIN_H
#	define TEST_GRAPHICS_TERRAIN_H

#include "simplexnoise.h"

namespace test {
	namespace graffiti {

enum Material
{
	Material_Void = 0,
	Material_Dirt,
	Material_Sand,
	Material_Count
};

class Terrain
{
public :
	Terrain(unsigned int side)
		:	m_size(side)
	{
		m_cells = new Material [m_size * m_size * m_size];
	}

	~Terrain()
	{
		delete [] m_cells;
	}

	void set(const glm::ivec3& pos, Material mat)
	{
		dwAssert(pos.x >= 0 && pos.x < (int)m_size, "");
		dwAssert(pos.y >= 0 && pos.y < (int)m_size, "");
		dwAssert(pos.z >= 0 && pos.z < (int)m_size, "");

		m_cells[pos.y * m_size * m_size + pos.z * m_size + pos.x] = mat;
	}

	Material get(const glm::ivec3& pos) const
	{
		dwAssert(pos.x >= 0 && pos.x < (int)m_size, "");
		dwAssert(pos.y >= 0 && pos.y < (int)m_size, "");
		dwAssert(pos.z >= 0 && pos.z < (int)m_size, "");

		return m_cells[pos.y * m_size * m_size + pos.z * m_size + pos.x];
	}

	unsigned int size() const
	{
		return m_size;
	}

	void generate()
	{
		srand(0);

		glm::ivec3 it;
		glm::vec3 itf;

		float caves, center_falloff, plateau_falloff, density;

		for (it.y = 0; it.y < (int)m_size; ++it.y)
		{
			itf.y = (float)it.y / (float)m_size;

			if (itf.y <= 0.8f)
			{
				plateau_falloff = 1.0f;
			}
			else if (0.8f < itf.y && itf.y < 0.9f)
			{
				plateau_falloff = 1.0f - (itf.y - 0.8f) * 10.0f;
			}
			else
			{
				plateau_falloff = 0.0f;
			}

			for (it.z = 0; it.z < (int)m_size; ++it.z)
			{
				itf.z = (float)it.z / (float)m_size;

				for (it.x = 0; it.x < (int)m_size; ++it.x)
				{
					itf.x = (float)it.x / (float)m_size;

					center_falloff = 0.1f / (
						powf((itf.x - 0.5f) * 1.5f, 2.0f) +
						powf((itf.y - 1.0f) * 0.8f, 2.0f) +
						powf((itf.z - 0.5f) * 1.5f, 2.0f));

					caves = powf(simplex_noise(1, itf.x * 5.0f, itf.y * 5.0f, itf.z * 5.0f), 3.0f);

					density = simplex_noise(5, itf.x, itf.y * 0.5f, itf.z) *
						center_falloff * plateau_falloff;

					density *= powf(
						noise((itf.x + 1.0f) * 3.0f, (itf.y + 1.0f) * 3.0f, (itf.z + 1.0f) * 3.0f) + 0.4f,
						1.8f );

					if ( caves < 0.5f)
					{
						density = 0.0f;
					}
					
					set(it, density > 3.1f ? Material_Dirt : Material_Void);
				}
			}
		}
	}

private :
	Material* m_cells;
	unsigned int m_size;
};

struct Vertex
{
	glm::vec3 position;
	glm::vec3 normal;
	glm::vec2 uv;
};

enum CubeFace
{
	CubeFace_Front = 0,
	CubeFace_Back,
	CubeFace_Left,
	CubeFace_Right,
	CubeFace_Top,
	CubeFace_Bottom,
	CubeFace_Count
};

void addQuad(const glm::vec3& offset, const glm::ivec4& indices, glm::vec3* positions, const glm::vec3& normal, unsigned int* indexBuffer, Vertex* vertexBuffer, unsigned int& verticesCount, unsigned int& indicesCount);
void addFace(const glm::vec3& position, Material mat, CubeFace face, unsigned int* indexBuffer, Vertex* vertexBuffer, unsigned int& verticesCount, unsigned int& indicesCount);

	} // namespace graffiti
} // namespace test

#endif // !TEST_GRAPHICS_TERRAIN_H
